/**
 * 通用滑动块队列
 * 张勇 41204@qq.com
 */
 
#pragma once

#include <stdint.h>
#include <string.h>
#include "utils.h"
#include "unios.h"

#ifdef __cplusplus
extern "C" {
#endif

// forward 声明: 块数据结构
typedef struct _shift_block shift_block_t;
/// 块类型
typedef uint8_t shift_block_type_t;

/// @brief 块类型：最大允许的块类型码(0 ~ 0x7F)
#define SB_TYPE_MAX			(0x7F)
/// @brief 块净荷数据最大允许的字节数
#define SB_PAYLOAD_MAX		(0x00FFFFFF)

/// @brief 块类型
shift_block_type_t SBType(shift_block_t* block);
/// @brief 净荷数据指针
uint8_t* SBData(shift_block_t* block);
/// @brief 净荷有效数据字节数
uint32_t SBDataLen(shift_block_t* block);

/// @brief 块是否已标记为清除
bool SBIsFree(shift_block_t* block);
/// @brief 块标记为清除
void SBFree(shift_block_t* block);

/// @brief 块是否被锁定
bool SBIsLocked(shift_block_t* block);
/// @brief 锁定块
void SBLock(shift_block_t* block);
/// @brief 解锁块
void SBUnlock(shift_block_t* block);


/// @brief 块队列数据结构
typedef struct _shift_block_queue {
	unios_critical_section_t css;///< 队列 Critical Section
    uint32_t size;               ///< 缓冲区尺寸
    uint8_t* const head;         ///< 指向缓冲区头部(也是队列头部)
    uint8_t* tail;               ///< 指向队列末尾
    const uint8_t* limit;        ///< 指向缓冲区末尾
} shift_block_queue_t;

/// @brief 通过宏静态定义一个队列
/// @param name: 队列指针变量名称
/// @param size: 缓冲区大小
#define SBQAllocStatic(name, size) \
	static uint8_t (__##name##Buffer__)[size]; \
	static shift_block_queue_t (__##name##Queue__) = { NULL, size, (__##name##Buffer__), (__##name##Buffer__), (__##name##Buffer__)+size }; \
	static shift_block_queue_t* name = &(__##name##Queue__)

/// @brief block 是否有效
bool SBIsValid(shift_block_queue_t* q, shift_block_t* block);

/// @brief 锁定队列
static inline std_err_t SBQLock(shift_block_queue_t* q) { return unios_critical_section_enter((q)->css, 1000); }
/// @brief 解锁队列
static inline void      SBQUnlock(shift_block_queue_t* q) { unios_critical_section_leave((q)->css); }

/// @brief 队列是否为空
static inline bool      SBQIsEmpty(shift_block_queue_t* q) { return ((q)->tail <= (q)->head); }
/// @brief 队列是否已满
static inline bool      SBQIsFull(shift_block_queue_t* q)  { return ((q)->tail >= (q)->limit); }

/// @brief 获取下一个非空的块
/// @param current 当前块
/// @return 当前块的下一个；如果 current 为 NULL，则返回最后一个；如果当前已经是最后一个了，则返回 NULL
shift_block_t* SBQNext(shift_block_queue_t* q, shift_block_t* current);

/// @brief 锁定队列，并在队列的末尾分配一个块
/// @param type 块类型
/// @param len 净荷长度
/// @return 新分配的块。如果队列空间长度不够，或块类型错误(<=0x7F)，则返回 NULL
shift_block_t* SBQAlloc(shift_block_queue_t* q, uint8_t type, uint32_t len);

/// @brief 提交块(提交的长度必须小于等于分配到的长度)，并解锁队列
/// @param block 通过 SBQAlloc() 分配到的块
/// @return 新创建的块
shift_block_t* SBQCommit(shift_block_queue_t* q, shift_block_t* block);

/// @brief 取消提交，并解锁队列
static inline void SBQUncommit(shift_block_queue_t* q, shift_block_t* block) { SBQUnlock(q); }

/// @brief 向队列尾部添加新块
shift_block_t* SBQAppend(shift_block_queue_t* q, uint8_t type, uint8_t len, uint8_t* payload);

/// @brief 清除块队列中的无效块，并压缩块队列
/// @attention 注意：因为可能要进行大量内存搬移，此操作可能比较耗时！可以先通过 SBFree() 将多个块标记为删除，然后再一次性调用 SBQPack() 以提升效率
void SBQPack(shift_block_queue_t* q);

/// @brief 清除块队列中的所有数据
void SBQClear(shift_block_queue_t* q);


#ifdef __cplusplus
}
#endif
